<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML
><HEAD
><TITLE
>Debugging</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK
REL="HOME"
TITLE="Advanced Bash-Scripting Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="Advanced Topics"
HREF="part5.html"><LINK
REL="PREVIOUS"
TITLE="Of Zeros and Nulls"
HREF="zeros.html"><LINK
REL="NEXT"
TITLE="Options"
HREF="options.html"></HEAD
><BODY
CLASS="CHAPTER"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>Advanced Bash-Scripting Guide: </TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="zeros.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
></TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="options.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="CHAPTER"
><H1
><A
NAME="DEBUGGING"
></A
>Chapter 32. Debugging</H1
><TABLE
BORDER="0"
WIDTH="100%"
CELLSPACING="0"
CELLPADDING="0"
CLASS="EPIGRAPH"
><TR
><TD
WIDTH="45%"
>&nbsp;</TD
><TD
WIDTH="45%"
ALIGN="LEFT"
VALIGN="TOP"
><I
><P
><I
>Debugging is twice as hard as writing the code in the first
        place. Therefore, if you write the code as cleverly as possible,
        you are, by definition, not smart enough to debug it.</I
></P
><P
><I
>--Brian Kernighan</I
></P
></I
></TD
></TR
></TABLE
><P
>The Bash shell contains no built-in debugger, and only bare-bones
	debugging-specific commands and constructs. Syntax errors or
	outright typos in the script generate cryptic error messages that
	are often of no help in debugging a non-functional script.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX74"
></A
><P
><B
>Example 32-1. A buggy script</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# ex74.sh

# This is a buggy script.
# Where, oh where is the error?

a=37

if [$a -gt 27 ]
then
  echo $a
fi  

exit $?   # 0! Why?</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>Output from script:
	<TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
><TT
CLASS="COMPUTEROUTPUT"
>./ex74.sh: [37: command not found</TT
></PRE
></FONT
></TD
></TR
></TABLE
>
        What's wrong with the above script? Hint: after the
        <I
CLASS="FIRSTTERM"
>if</I
>.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="MISSINGKEYWORD"
></A
><P
><B
>Example 32-2. Missing <A
HREF="internal.html#KEYWORDREF"
>keyword</A
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# missing-keyword.sh
# What error message will this script generate? And why?

for a in 1 2 3
do
  echo "$a"
# done     # Required keyword 'done' commented out in line 8.

exit 0     # Will not exit here!

# === #

# From command line, after script terminates:
  echo $?    # 2</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>Output from script:
	<TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
><TT
CLASS="COMPUTEROUTPUT"
>missing-keyword.sh: line 10: syntax error: unexpected end of file</TT
>
	</PRE
></FONT
></TD
></TR
></TABLE
>
	Note that the error message does <EM
>not</EM
> necessarily
	reference the line in which the error occurs, but the line where the
	Bash interpreter finally becomes aware of the error.
	</P
><P
>Error messages may disregard comment lines in a script when
        reporting the line number of a syntax error.</P
><P
>What if the script executes, but does not work as expected? This is the
	all too familiar logic error.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX75"
></A
><P
><B
>Example 32-3. <I
CLASS="FIRSTTERM"
>test24</I
>: another buggy script</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash

#  This script is supposed to delete all filenames in current directory
#+ containing embedded spaces.
#  It doesn't work.
#  Why not?


badname=`ls | grep ' '`

# Try this:
# echo "$badname"

rm "$badname"

exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>Try to find out what's wrong with <A
HREF="debugging.html#EX75"
>Example 32-3</A
>
	by uncommenting the <TT
CLASS="USERINPUT"
><B
>echo "$badname"</B
></TT
> line. Echo
	statements are useful for seeing whether what you expect is
	actually what you get.</P
><P
>In this particular case, <TT
CLASS="USERINPUT"
><B
>rm "$badname"</B
></TT
>
	will not give the desired results because
	<TT
CLASS="VARNAME"
>$badname</TT
> should not be quoted. Placing it
	in quotes ensures that <B
CLASS="COMMAND"
>rm</B
> has only one
	argument (it will match only one filename). A partial fix
	is to remove to quotes from <TT
CLASS="VARNAME"
>$badname</TT
> and
	to reset <TT
CLASS="VARNAME"
>$IFS</TT
> to contain only a newline,
	<TT
CLASS="USERINPUT"
><B
>IFS=$'\n'</B
></TT
>. However, there are simpler
	ways of going about it.
	<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
># Correct methods of deleting filenames containing spaces.
rm *\ *
rm *" "*
rm *' '*
# Thank you. S.C.</PRE
></FONT
></TD
></TR
></TABLE
>
	
	</P
><P
>Summarizing the symptoms of a buggy script,
	<P
></P
><OL
TYPE="1"
><LI
><P
>It bombs with a <SPAN
CLASS="QUOTE"
>"<SPAN
CLASS="ERRORNAME"
>syntax error</SPAN
>"</SPAN
> message, or</P
></LI
><LI
><P
>It runs, but does not work as expected 
	      (<SPAN
CLASS="ERRORNAME"
>logic error</SPAN
>).</P
></LI
><LI
><P
>It runs, works as expected, but has nasty side effects
	      (<SPAN
CLASS="ERRORNAME"
>logic bomb</SPAN
>).</P
></LI
></OL
>
      </P
><P
><A
NAME="DEBUGTOOLS"
></A
></P
><P
>Tools for debugging non-working scripts include
	<P
></P
><OL
TYPE="1"
><LI
><P
>Inserting <A
HREF="internal.html#ECHOREF"
>echo</A
>
	      statements at critical points in the script to trace the
	      variables, and otherwise give a snapshot of what is going
	      on.</P
><DIV
CLASS="TIP"
><P
></P
><TABLE
CLASS="TIP"
WIDTH="90%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/tip.gif"
HSPACE="5"
ALT="Tip"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>Even better is an <B
CLASS="COMMAND"
>echo</B
> that echoes
	      only when <I
CLASS="FIRSTTERM"
>debug</I
> is on.</P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>### debecho (debug-echo), by Stefano Falsetto ###
### Will echo passed parameters only if DEBUG is set to a value. ###
debecho () {
  if [ ! -z "$DEBUG" ]; then
     echo "$1" &#62;&#38;2
     #         ^^^ to stderr
  fi
}

DEBUG=on
Whatever=whatnot
debecho $Whatever   # whatnot

DEBUG=
Whatever=notwhat
debecho $Whatever   # (Will not echo.)</PRE
></FONT
></TD
></TR
></TABLE
></P
></TD
></TR
></TABLE
></DIV
></LI
><LI
><P
>Using the <A
HREF="extmisc.html#TEEREF"
>tee</A
> filter
	      to check processes or data flows at critical points.</P
></LI
><LI
><P
>Setting option flags <TT
CLASS="OPTION"
>-n -v -x</TT
></P
><P
><TT
CLASS="USERINPUT"
><B
>sh -n scriptname</B
></TT
> checks for
	      syntax errors without actually running the script. This is
	      the equivalent of inserting <TT
CLASS="USERINPUT"
><B
>set -n</B
></TT
> or
	      <TT
CLASS="USERINPUT"
><B
>set -o noexec</B
></TT
> into the script. Note
	      that certain types of syntax errors can slip past this
	      check.</P
><P
><TT
CLASS="USERINPUT"
><B
>sh -v scriptname</B
></TT
> echoes each
	      command before executing it. This is the equivalent of
	      inserting <TT
CLASS="USERINPUT"
><B
>set -v</B
></TT
> or <TT
CLASS="USERINPUT"
><B
>set
	      -o verbose</B
></TT
> in the script.</P
><P
>The <TT
CLASS="OPTION"
>-n</TT
> and <TT
CLASS="OPTION"
>-v</TT
>
	      flags work well together. <TT
CLASS="USERINPUT"
><B
>sh -nv
	      scriptname</B
></TT
> gives a verbose syntax check.</P
><P
><TT
CLASS="USERINPUT"
><B
>sh -x scriptname</B
></TT
> echoes the result each
	      command, but in an abbreviated manner. This is the equivalent of
	      inserting <TT
CLASS="USERINPUT"
><B
>set -x</B
></TT
> or 
	      <TT
CLASS="USERINPUT"
><B
>set -o xtrace</B
></TT
> in the script.</P
><P
><A
NAME="UNDVARERR"
></A
></P
><P
>Inserting <TT
CLASS="USERINPUT"
><B
>set -u</B
></TT
> or 
		<TT
CLASS="USERINPUT"
><B
>set -o nounset</B
></TT
> in the script runs it, but
		gives an <SPAN
CLASS="ERRORNAME"
>unbound variable</SPAN
> error message
		and aborts the script.
		<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>set -u   # Or   set -o nounset

# Setting a variable to null will not trigger the error/abort.
# unset_var=

echo $unset_var   # Unset (and undeclared) variable.

echo "Should not echo!"

# sh t2.sh
# t2.sh: line 6: unset_var: unbound variable</PRE
></FONT
></TD
></TR
></TABLE
></P
></LI
><LI
><P
>Using an <SPAN
CLASS="QUOTE"
>"assert"</SPAN
> function to test a
	      variable or condition at critical points in a script. (This is
	      an idea borrowed from C.)</P
><DIV
CLASS="EXAMPLE"
><A
NAME="ASSERT"
></A
><P
><B
>Example 32-4. Testing a condition with an
	      <I
CLASS="FIRSTTERM"
>assert</I
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# assert.sh

#######################################################################
assert ()                 #  If condition false,
{                         #+ exit from script
                          #+ with appropriate error message.
  E_PARAM_ERR=98
  E_ASSERT_FAILED=99


  if [ -z "$2" ]          #  Not enough parameters passed
  then                    #+ to assert() function.
    return $E_PARAM_ERR   #  No damage done.
  fi

  lineno=$2

  if [ ! $1 ] 
  then
    echo "Assertion failed:  \"$1\""
    echo "File \"$0\", line $lineno"    # Give name of file and line number.
    exit $E_ASSERT_FAILED
  # else
  #   return
  #   and continue executing the script.
  fi  
} # Insert a similar assert() function into a script you need to debug.    
#######################################################################


a=5
b=4
condition="$a -lt $b"     #  Error message and exit from script.
                          #  Try setting "condition" to something else
                          #+ and see what happens.

assert "$condition" $LINENO
# The remainder of the script executes only if the "assert" does not fail.


# Some commands.
# Some more commands . . .
echo "This statement echoes only if the \"assert\" does not fail."
# . . .
# More commands . . .

exit $?</PRE
></FONT
></TD
></TR
></TABLE
></DIV
></LI
><LI
><P
>Using the <A
HREF="internalvariables.html#LINENOREF"
>$LINENO</A
>
	      variable and the <A
HREF="internal.html#CALLERREF"
>caller</A
>
	      builtin.</P
></LI
><LI
><P
><A
NAME="DEBUGTRAP"
></A
>Trapping at exit.</P
><P
>The <A
HREF="internal.html#EXITREF"
>exit</A
> command in a script
	      triggers a signal <SPAN
CLASS="RETURNVALUE"
>0</SPAN
>, terminating
	      the process, that is, the script itself.

	      <A
NAME="AEN19460"
HREF="#FTN.AEN19460"
><SPAN
CLASS="footnote"
>[1]</SPAN
></A
>

	      It is often useful to trap the
	      <I
CLASS="FIRSTTERM"
>exit</I
>, forcing a <SPAN
CLASS="QUOTE"
>"printout"</SPAN
>
	      of variables, for example. The <I
CLASS="FIRSTTERM"
>trap</I
>
	      must be the first command in the script.</P
></LI
></OL
>
      </P
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
><A
NAME="TRAPREF1"
></A
>Trapping signals</B
></P
><DL
><DT
><B
CLASS="COMMAND"
>trap</B
></DT
><DD
><P
>Specifies an action on receipt of a
	        signal; also useful for debugging.</P
><P
><A
NAME="SIGNALD"
></A
></P
><TABLE
CLASS="SIDEBAR"
BORDER="1"
CELLPADDING="5"
><TR
><TD
><DIV
CLASS="SIDEBAR"
><A
NAME="AEN19477"
></A
><P
></P
><P
>A <I
CLASS="FIRSTTERM"
>signal</I
> is a message
		sent to a process, either by the kernel or another
		process, telling it to take some specified action
		(usually to terminate).  For example, hitting a
		<A
HREF="special-chars.html#CTLCREF"
>Control-C</A
>
		sends a user interrupt, an INT signal, to a running
		program.</P
><P
></P
></DIV
></TD
></TR
></TABLE
><P
><EM
>A simple instance:</EM
>
		  <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>trap '' 2
# Ignore interrupt 2 (Control-C), with no action specified. 

trap 'echo "Control-C disabled."' 2
# Message when Control-C pressed.</PRE
></FONT
></TD
></TR
></TABLE
>
	      </P
></DD
></DL
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="EX76"
></A
><P
><B
>Example 32-5. Trapping at exit</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# Hunting variables with a trap.

trap 'echo Variable Listing --- a = $a  b = $b' EXIT
#  EXIT is the name of the signal generated upon exit from a script.
#
#  The command specified by the "trap" doesn't execute until
#+ the appropriate signal is sent.

echo "This prints before the \"trap\" --"
echo "even though the script sees the \"trap\" first."
echo

a=39

b=36

exit 0
#  Note that commenting out the 'exit' command makes no difference,
#+ since the script exits in any case after running out of commands.</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="ONLINE"
></A
><P
><B
>Example 32-6. Cleaning up after <B
CLASS="KEYCAP"
>Control-C</B
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# logon.sh: A quick 'n dirty script to check whether you are on-line yet.

umask 177  # Make sure temp files are not world readable.


TRUE=1
LOGFILE=/var/log/messages
#  Note that $LOGFILE must be readable
#+ (as root, chmod 644 /var/log/messages).
TEMPFILE=temp.$$
#  Create a "unique" temp file name, using process id of the script.
#     Using 'mktemp' is an alternative.
#     For example:
#     TEMPFILE=`mktemp temp.XXXXXX`
KEYWORD=address
#  At logon, the line "remote IP address xxx.xxx.xxx.xxx"
#                      appended to /var/log/messages.
ONLINE=22
USER_INTERRUPT=13
CHECK_LINES=100
#  How many lines in log file to check.

trap 'rm -f $TEMPFILE; exit $USER_INTERRUPT' TERM INT
#  Cleans up the temp file if script interrupted by control-c.

echo

while [ $TRUE ]  #Endless loop.
do
  tail -n $CHECK_LINES $LOGFILE&#62; $TEMPFILE
  #  Saves last 100 lines of system log file as temp file.
  #  Necessary, since newer kernels generate many log messages at log on.
  search=`grep $KEYWORD $TEMPFILE`
  #  Checks for presence of the "IP address" phrase,
  #+ indicating a successful logon.

  if [ ! -z "$search" ] #  Quotes necessary because of possible spaces.
  then
     echo "On-line"
     rm -f $TEMPFILE    #  Clean up temp file.
     exit $ONLINE
  else
     echo -n "."        #  The -n option to echo suppresses newline,
                        #+ so you get continuous rows of dots.
  fi

  sleep 1  
done  


#  Note: if you change the KEYWORD variable to "Exit",
#+ this script can be used while on-line
#+ to check for an unexpected logoff.

# Exercise: Change the script, per the above note,
#           and prettify it.

exit 0


# Nick Drage suggests an alternate method:

while true
  do ifconfig ppp0 | grep UP 1&#62; /dev/null &#38;&#38; echo "connected" &#38;&#38; exit 0
  echo -n "."   # Prints dots (.....) until connected.
  sleep 2
done

# Problem: Hitting Control-C to terminate this process may be insufficient.
#+         (Dots may keep on echoing.)
# Exercise: Fix this.



# Stephane Chazelas has yet another alternative:

CHECK_INTERVAL=1

while ! tail -n 1 "$LOGFILE" | grep -q "$KEYWORD"
do echo -n .
   sleep $CHECK_INTERVAL
done
echo "On-line"

# Exercise: Discuss the relative strengths and weaknesses
#           of each of these various approaches.</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="PROGRESSBAR2"
></A
><P
><B
>Example 32-7. A Simple Implementation of a Progress Bar</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#! /bin/bash
# progress-bar2.sh
# Author: Graham Ewart (with reformatting by ABS Guide author).
# Used in ABS Guide with permission (thanks!).

# Invoke this script with bash. It doesn't work with sh.

interval=1
long_interval=10

{
     trap "exit" SIGUSR1
     sleep $interval; sleep $interval
     while true
     do
       echo -n '.'     # Use dots.
       sleep $interval
     done; } &#38;         # Start a progress bar as a background process.

pid=$!
trap "echo !; kill -USR1 $pid; wait $pid"  EXIT        # To handle ^C.

echo -n 'Long-running process '
sleep $long_interval
echo ' Finished!'

kill -USR1 $pid
wait $pid              # Stop the progress bar.
trap EXIT

exit $?</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="NOTE"
><P
></P
><TABLE
CLASS="NOTE"
WIDTH="100%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/note.gif"
HSPACE="5"
ALT="Note"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>The <TT
CLASS="OPTION"
>DEBUG</TT
> argument to
	<B
CLASS="COMMAND"
>trap</B
> causes a specified action to execute
	after every command in a script. This permits tracing variables,
	for example.

      <DIV
CLASS="EXAMPLE"
><A
NAME="VARTRACE"
></A
><P
><B
>Example 32-8. Tracing a variable</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash

trap 'echo "VARIABLE-TRACE&#62; \$variable = \"$variable\""' DEBUG
# Echoes the value of $variable after every command.

variable=29; line=$LINENO

echo "  Just initialized \$variable to $variable in line number $line."

let "variable *= 3"; line=$LINENO
echo "  Just multiplied \$variable by 3 in line number $line."

exit 0

#  The "trap 'command1 . . . command2 . . .' DEBUG" construct is
#+ more appropriate in the context of a complex script,
#+ where inserting multiple "echo $variable" statements might be
#+ awkward and time-consuming.

# Thanks, Stephane Chazelas for the pointer.


Output of script:

VARIABLE-TRACE&#62; $variable = ""
VARIABLE-TRACE&#62; $variable = "29"
  Just initialized $variable to 29.
VARIABLE-TRACE&#62; $variable = "29"
VARIABLE-TRACE&#62; $variable = "87"
  Just multiplied $variable by 3.
VARIABLE-TRACE&#62; $variable = "87"</PRE
></FONT
></TD
></TR
></TABLE
></DIV
>

      </P
></TD
></TR
></TABLE
></DIV
><P
>Of course, the <B
CLASS="COMMAND"
>trap</B
> command has other uses
        aside from debugging, such as disabling certain keystrokes within a
	script (see <A
HREF="contributed-scripts.html#STOPWATCH"
>Example A-43</A
>).</P
><DIV
CLASS="EXAMPLE"
><A
NAME="MULTIPLEPROC"
></A
><P
><B
>Example 32-9. Running multiple processes (on an SMP box)</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# parent.sh
# Running multiple processes on an SMP box.
# Author: Tedman Eng

#  This is the first of two scripts,
#+ both of which must be present in the current working directory.




LIMIT=$1         # Total number of process to start
NUMPROC=4        # Number of concurrent threads (forks?)
PROCID=1         # Starting Process ID
echo "My PID is $$"

function start_thread() {
        if [ $PROCID -le $LIMIT ] ; then
                ./child.sh $PROCID&#38;
                let "PROCID++"
        else
           echo "Limit reached."
           wait
           exit
        fi
}

while [ "$NUMPROC" -gt 0 ]; do
        start_thread;
        let "NUMPROC--"
done


while true
do

trap "start_thread" SIGRTMIN

done

exit 0



# ======== Second script follows ========


#!/bin/bash
# child.sh
# Running multiple processes on an SMP box.
# This script is called by parent.sh.
# Author: Tedman Eng

temp=$RANDOM
index=$1
shift
let "temp %= 5"
let "temp += 4"
echo "Starting $index  Time:$temp" "$@"
sleep ${temp}
echo "Ending $index"
kill -s SIGRTMIN $PPID

exit 0


# ======================= SCRIPT AUTHOR'S NOTES ======================= #
#  It's not completely bug free.
#  I ran it with limit = 500 and after the first few hundred iterations,
#+ one of the concurrent threads disappeared!
#  Not sure if this is collisions from trap signals or something else.
#  Once the trap is received, there's a brief moment while executing the
#+ trap handler but before the next trap is set.  During this time, it may
#+ be possible to miss a trap signal, thus miss spawning a child process.

#  No doubt someone may spot the bug and will be writing 
#+ . . . in the future.



# ===================================================================== #



# ----------------------------------------------------------------------#



#################################################################
# The following is the original script written by Vernia Damiano.
# Unfortunately, it doesn't work properly.
#################################################################

#!/bin/bash

#  Must call script with at least one integer parameter
#+ (number of concurrent processes).
#  All other parameters are passed through to the processes started.


INDICE=8        # Total number of process to start
TEMPO=5         # Maximum sleep time per process
E_BADARGS=65    # No arg(s) passed to script.

if [ $# -eq 0 ] # Check for at least one argument passed to script.
then
  echo "Usage: `basename $0` number_of_processes [passed params]"
  exit $E_BADARGS
fi

NUMPROC=$1              # Number of concurrent process
shift
PARAMETRI=( "$@" )      # Parameters of each process

function avvia() {
         local temp
         local index
         temp=$RANDOM
         index=$1
         shift
         let "temp %= $TEMPO"
         let "temp += 1"
         echo "Starting $index Time:$temp" "$@"
         sleep ${temp}
         echo "Ending $index"
         kill -s SIGRTMIN $$
}

function parti() {
         if [ $INDICE -gt 0 ] ; then
              avvia $INDICE "${PARAMETRI[@]}" &#38;
                let "INDICE--"
         else
                trap : SIGRTMIN
         fi
}

trap parti SIGRTMIN

while [ "$NUMPROC" -gt 0 ]; do
         parti;
         let "NUMPROC--"
done

wait
trap - SIGRTMIN

exit $?

: &#60;&#60;SCRIPT_AUTHOR_COMMENTS
I had the need to run a program, with specified options, on a number of
different files, using a SMP machine. So I thought [I'd] keep running
a specified number of processes and start a new one each time . . . one
of these terminates.

The "wait" instruction does not help, since it waits for a given process
or *all* process started in background. So I wrote [this] bash script
that can do the job, using the "trap" instruction.
  --Vernia Damiano
SCRIPT_AUTHOR_COMMENTS</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="NOTE"
><P
></P
><TABLE
CLASS="NOTE"
WIDTH="100%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/note.gif"
HSPACE="5"
ALT="Note"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
><TT
CLASS="USERINPUT"
><B
>trap '' SIGNAL</B
></TT
> (two adjacent
	apostrophes) disables SIGNAL for the remainder of the
	script. <TT
CLASS="USERINPUT"
><B
>trap SIGNAL</B
></TT
> restores
	the functioning of SIGNAL once more. This is useful to
	protect a critical portion of a script from an undesirable
	interrupt.</P
></TD
></TR
></TABLE
></DIV
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>	trap '' 2  # Signal 2 is Control-C, now disabled.
	command
	command
	command
	trap 2     # Reenables Control-C
	</PRE
></FONT
></TD
></TR
></TABLE
></P
><TABLE
CLASS="SIDEBAR"
BORDER="1"
CELLPADDING="5"
><TR
><TD
><DIV
CLASS="SIDEBAR"
><A
NAME="AEN19513"
></A
><P
></P
><P
><A
HREF="bashver3.html#BASH3REF"
>Version 3</A
> of Bash adds the
	  following <A
HREF="internalvariables.html#INTERNALVARIABLES1"
>internal
	  variables</A
> for use by the debugger.

       <P
></P
><OL
TYPE="1"
><LI
><P
><TT
CLASS="VARNAME"
>$BASH_ARGC</TT
></P
><P
>Number of command-line arguments passed to script,
	     similar to <A
HREF="internalvariables.html#CLACOUNTREF"
><TT
CLASS="VARNAME"
>$#</TT
></A
>.</P
></LI
><LI
><P
><TT
CLASS="VARNAME"
>$BASH_ARGV</TT
></P
><P
>Final command-line parameter passed to script, equivalent
	     <A
HREF="othertypesv.html#LASTARGREF"
><TT
CLASS="VARNAME"
>${!#}</TT
></A
>.</P
></LI
><LI
><P
><TT
CLASS="VARNAME"
>$BASH_COMMAND</TT
></P
><P
>Command currently executing.</P
></LI
><LI
><P
><TT
CLASS="VARNAME"
>$BASH_EXECUTION_STRING</TT
></P
><P
>The <I
CLASS="FIRSTTERM"
>option string</I
> following the
	     <TT
CLASS="OPTION"
>-c</TT
> <A
HREF="bash-options.html#CLOPTS"
>option</A
>
	     to Bash.</P
></LI
><LI
><P
><TT
CLASS="VARNAME"
>$BASH_LINENO</TT
></P
><P
>In a <A
HREF="functions.html#FUNCTIONREF"
>function</A
>,
	     indicates the line number of the function call.</P
></LI
><LI
><P
><TT
CLASS="VARNAME"
>$BASH_REMATCH</TT
></P
><P
>Array variable associated with <B
CLASS="COMMAND"
>=~</B
>
	   <A
HREF="bashver3.html#REGEXMATCHREF"
>conditional regex
	   matching</A
>.</P
></LI
><LI
><P
><A
NAME="BASHSOURCEREF"
></A
></P
><P
><TT
CLASS="VARNAME"
>$BASH_SOURCE</TT
></P
><P
>This is the name of the script, usually the same as
	     <A
HREF="othertypesv.html#ARG0"
>$0</A
>.</P
></LI
><LI
><P
>	 <A
HREF="internalvariables.html#BASHSUBSHELLREF"
><TT
CLASS="VARNAME"
>$BASH_SUBSHELL</TT
></A
></P
></LI
></OL
></P
><P
></P
></DIV
></TD
></TR
></TABLE
></DIV
><H3
CLASS="FOOTNOTES"
>Notes</H3
><TABLE
BORDER="0"
CLASS="FOOTNOTES"
WIDTH="100%"
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN19460"
HREF="debugging.html#AEN19460"
><SPAN
CLASS="footnote"
>[1]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>By convention, <TT
CLASS="REPLACEABLE"
><I
>signal
		0</I
></TT
> is assigned to <A
HREF="exit-status.html#EXITCOMMANDREF"
>exit</A
>.  </P
></TD
></TR
></TABLE
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="zeros.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="options.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Of Zeros and Nulls</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="part5.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Options</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>